package com.raccoonagents.tools.cascet2d.model.stage;

import java.util.ArrayList;
import java.util.Collection;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

import com.raccoonagents.tools.cascet2d.interfaces.IRenderableStageElement;
import com.raccoonagents.tools.cascet2d.interfaces.IStageLocker;
import com.raccoonagents.tools.cascet2d.interfaces.IUpdateable;
import com.raccoonagents.tools.cascet2d.interfaces.rendering.IWindow;
import com.raccoonagents.tools.cascet2d.model.Vector2f;
import com.raccoonagents.tools.cascet2d.util.ElementUtils;
import com.raccoonagents.tools.cascet2d.util.GraphicUtil;

public abstract class Stage extends Root{
	private IStageLocker lock;
	private final Layer hoveringLayer = new Layer();
	private final Vector2f location = new Vector2f(0.0f,0.0f);
	protected int bg =-1;
	protected long delta = 0;
	private long oldTime = 0;
	private long currentTime = 0;
	protected boolean loadIdentityMatrix = true;
	protected Vector2f bgSize = null;


	public Vector2f getBgSize() {
		return bgSize;
	}

	public void setBgSize(final Vector2f bgSize) {
		this.bgSize = bgSize;
	}


	private State state = State.RUNNING;
	
	public static enum State{		
		LOCKED,
		RUNNING;
	}		
	/**
	 * pauses the stage with this element. Only given element an it's children
	 * will keep updating their state, until lock is released.
	 * @param lockingElement
	 */
	public void lock(final IStageLocker lockingElement){
		this.lock = lockingElement;
		state = State.LOCKED;
	}

	/**
	 * 
	 * @return true, if stage is auto-loading identity matrix at start of render sequence.
	 */
	public boolean isLoadIdentityMatrix() {
		return loadIdentityMatrix;
	}
	/**
	 * Identity matrix is loaded by default at rendering automatically. If you wish to manually do it,
	 * this needs to be set to false.
	 */
	public void setLoadIdentityMatrix(final boolean loadIdentityMatrix) {
		this.loadIdentityMatrix = loadIdentityMatrix;
	}
	
	public void releaseLock(){
		this.lock = null;
		state = State.RUNNING;
	}
	
	public boolean isPaused(){
		return state == State.LOCKED;
	}
	
	public State getState() {
		return state;
	}
	
	public void setState(final State state) {
		this.state = state;
	}
	
	public void addChild(final IWindow dialog){
		
		hoveringLayer.addChild(dialog);
	}

	@Override
	public final Vector2f getLocation() {
		return location;
	}
	
	public abstract void initialize();
	public abstract void run();
	public abstract void end();
	
	
	/**
	 * performs update for all elements implementing IUpdateable in the stage recursively.
	 */
	private Collection<IRenderableStageElement> allChilds = new ArrayList<IRenderableStageElement>();
	public final void update(){
		ElementUtils.updateContainerRelations(this);
		if(state == State.RUNNING){
			//final Collection<IRenderableStageElement> allChilds = getElementsRecursive();		
			allChilds = getElementsRecursive();
			for(final IRenderableStageElement child : allChilds){
				if(IUpdateable.class.isAssignableFrom(child.getClass())){
					((IUpdateable)child).update(); 				
				}
			}			
			allChilds.clear();

			hoveringLayer.update();
		}else if(state == State.LOCKED){
			lock.updateLock();
		}	
	}


	@Override
	public final Vector2f getWorldCoordinates(final Vector2f localCoordinates) {
		return localCoordinates;
	}
	
	public abstract void draw();
	

	@Override
	public final void render() {
		if(loadIdentityMatrix){
			GL11.glLoadIdentity();
		}
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		if(bg != -1){
			if(bgSize == null){
				GraphicUtil.renderBG(bg);				
			}else{
				GraphicUtil.renderBG(bg, (int)bgSize.x, (int)bgSize.y);
			}
		}else{
			GL11.glColor3f(0.0f,0.0f,0.0f);
			final DisplayMode d = Display.getDisplayMode();
			GL11.glBegin(GL11.GL_QUADS);
			GL11.glVertex2f(0.0f, 0.0f);
			GL11.glVertex2f(d.getWidth(), 0.0f);
			GL11.glVertex2f(d.getWidth(),d.getHeight());
			GL11.glVertex2f(0.0f, d.getHeight());
			
			GL11.glEnd();
		}
		draw();
		
		for(final IRenderableStageElement child : children){
			child.render();
		}
		
		hoveringLayer.render();
		/*
		try {
			Thread.sleep(100);
		} catch (final InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		//oldTime = System.currentTimeMillis();
		//hoveringLayer.render();
		Display.update();
		oldTime = currentTime;
		currentTime = System.currentTimeMillis();
		if(oldTime > 0){
			delta = currentTime - oldTime;			
		}else{
			delta = 0;
		}
	}
}
